package cn.gzjp.common.utils.pay;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.gzjp.common.utils.pay.enums.OrderStatus;
import cn.gzjp.common.utils.pay.security.MD5;


public final class PayUtils {

	private static Logger log = LoggerFactory.getLogger(PayUtils.class);

	public static final String JSAPI = "JSAPI";

	private PayUtils() {
		throw new RuntimeException("can't init");
	}

	public static String createNoncestr() {
		return UUID.randomUUID().toString().replaceAll("\\-", "");
	}

	private static String formatBizQueryParaMap(Map<String, String> paraMap,
			boolean urlencode) {
		StringBuilder sb = new StringBuilder();
		TreeMap<String, String> sortMap = new TreeMap<String, String>(paraMap);
		if (urlencode) {
			try {
				for (String key : sortMap.keySet()) {
					sb.append(key)
							.append("=")
							.append(URLEncoder.encode(sortMap.get(key), "UTF-8"))
							.append("&");
				}
			} catch (UnsupportedEncodingException e) {
				log.error(e.getMessage(), e);
			}
		} else {
			for (String key : sortMap.keySet()) {
				sb.append(key).append("=").append(sortMap.get(key)).append("&");
			}
		}
		if (sb.length() > 0) {
			sb.deleteCharAt(sb.length() - 1);
		}
		return sb.toString();
	}

	public static String getSign(Map<String, String> params, String key) {
		StringBuilder sb = new StringBuilder();
		sb.append(formatBizQueryParaMap(params, false));

		sb.append("&key=").append(key);

		String result = null;
		try {
			result = MD5.getMD5(sb.toString());
		} catch (NoSuchAlgorithmException e) {
			log.error(e.getMessage(), e);
			return result;
		}

		return result.toUpperCase();
	}

	/**
	 * 作用：以post方式提交xml到对应的接口url
	 * 
	 * @param xml
	 * @param url
	 * @return
	 */
	public static String postXmlCurl(String xml, String url) {
		HttpClient client = HttpClients.createSystem();
		HttpPost post = new HttpPost(url);
		String result = null;
		try {
			post.setEntity(new StringEntity(xml));
			HttpResponse httpResponse = client.execute(post);
			if (httpResponse.getStatusLine().getStatusCode() == 200) {
				HttpEntity httpEntity = httpResponse.getEntity();
				result = EntityUtils.toString(httpEntity);// 取出应答字符串
			}
		} catch (UnsupportedEncodingException e) {
			log.error(e.getMessage(), e);
		} catch (ClientProtocolException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		}
		return result;
	}

	/**
	 * 作用：非使用使用证书，以post方式提交xml到对应的接口url 获取 预支付ID时候使用
	 * 
	 * @param xml
	 * @param url
	 * 
	 * @return
	 */
	private static String postXmlSSLCurl(String xml, String url) {
		try {
			return HTTPSRequest.httpsRequest(url, "POST", xml);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 作用：使用证书，以post方式提交xml到对应的接口url 转账 冲正的时候使用
	 * 
	 * @param xml
	 * @param url
	 * @param path
	 *            证书路径
	 * @return
	 */
	private static String postXmlSSLCurl(String xml, String url, String sig,
			InputStream in) {
		try {
			return HTTPSRequest.httpsRequestSec(url, "POST", xml, sig, in);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 创建xml
	 * 
	 * @param outTradeNo
	 * @param body
	 * @param totalFee
	 * @param notifyUrl
	 * @param tradeType
	 * @param openid
	 * @param ip
	 * @return
	 */
	private static String createXml(String nonceStr, String outTradeNo,
			String body, String totalFee, String notifyUrl, String tradeType,
			String openid, String ip, String sign, String appId, String macId) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("out_trade_no", outTradeNo);
		params.put("body", body);
		params.put("total_fee", totalFee);
		params.put("notify_url", notifyUrl);
		params.put("trade_type", tradeType);
		params.put("openid", openid);
		params.put("appid", appId);// 公众账号ID
		params.put("mch_id", macId);// 商户号
		params.put("nonce_str", nonceStr);// 随机字符串
		params.put("sign", sign);// 签名
		return XmlUtils.mapToXml(params);
	}

	/**
	 * 作用：使用证书post请求xml
	 */
	private static String postXmlSSL(String nonceStr, String url,
			String outTradeNo, String body, String totalFee, String notifyUrl,
			String tradeType, String openid, String ip, String sign,
			String appId, String macId) {
		String xml = createXml(nonceStr, outTradeNo, body, totalFee, notifyUrl,
				tradeType, openid, ip, sign, appId, macId);
		return postXmlSSLCurl(xml, url);
	}

	/**
	 * 退款接口
	 * <p>
	 * 退款有一定延时，用零钱支付的退款,20 分钟内到账，银行卡支付的退款 3 个工作日后重新查询退款状态。
	 * </p>
	 * 
	 * @param transaction_id 微信生成的订单号，在支付通知中有返回
	 * @param outRefundNo  商户系统内部的退款单号，商户系统内部唯一，同一退款单号多次请求只退一笔
	 * @param totalFee
	 *            总金额 （分）
	 * @param refundFee
	 *            退款金额（分）
	 * @return 退款申请提交成功返回true，反之返回false
	 */
	public static boolean refund(String transaction_id, String outRefundNo,
			int totalFee, int refundFee, String appId, String mchId,String key,String mpAccount) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("transaction_id", transaction_id);
		params.put("out_refund_no", outRefundNo);
		params.put("total_fee", String.valueOf(totalFee));
		params.put("refund_fee", String.valueOf(refundFee));
		params.put("appid", appId);
		params.put("mch_id", mchId);
		params.put("op_user_id",mchId);
		params.put("nonce_str", PayUtils.createNoncestr());
		params.put("sign", PayUtils.getSign(params,key));

		String xml = XmlUtils.mapToXml(params);
		
		
		  FileInputStream instream = null;
		try {
			if(mpAccount==null){
				instream = new FileInputStream(new File("E:\\aisihair\\apiclient_cert.p12"));
			}else{
				 instream = new FileInputStream(new File("/usr/cert/"+mpAccount+"/apiclient_cert.p12")); // 此处为证书所放的绝对路径
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String result = PayUtils.postXmlSSLCurl(xml, PayConfig.REFUND_URL,mchId,instream);
		log.info(result);
		Map<String, String> resultMap = XmlUtils.xmlToMap(result);
		return "SUCCESS".equals(resultMap.get("return_code"));
	}

	/**
	 * 订单查询
	 * 
	 * @param outTradeNo
	 *            订单号
	 * @return
	 */
	public static OrderStatus orderQuery(String wxOrderNo, String appid,
			String mchid, String key) {
		TreeMap<String, String> params = new TreeMap<String, String>();
		params.put("transaction_id", wxOrderNo);
		params.put("appid", appid);
		params.put("mch_id", mchid);
		params.put("nonce_str", PayUtils.createNoncestr());
		params.put("sign", PayUtils.getSign(params, key));

		String xml = XmlUtils.mapToXml(params);
		String result = PayUtils.postXmlSSLCurl(xml, PayConfig.QUERY_ORDER);

		if (result == null)
			return OrderStatus.ERROR;

		Map<String, String> resultMap = XmlUtils.xmlToMap(result);
		if (null == resultMap.get("return_code"))
			return OrderStatus.ERROR;
		if (!"SUCCESS".equals(resultMap.get("return_code")))
			return OrderStatus.ERROR;
		if (!"SUCCESS".equals(resultMap.get("result_code")))
			return OrderStatus.ERROR;
			
		String status = resultMap.get("trade_state");

		log.info(wxOrderNo+","+appid+","+ mchid +","+ key+","+status);
		
		if(StringUtils.isEmpty(status)){
			return OrderStatus.ERROR;
		}

		return OrderStatus.valueOf(status);
	}

	public static void main(String[] args) {

		String wxorderNo = "1004470424201412260008414555";
		String appid = "wx77b237b2c467a45d";

		String mchId = "10059176";
		
		String busId="1225449401";
		String key = "4ab90c18dda7b307e670dfad0d6fe2c2";
		
		refund(wxorderNo,"1584256123331211",1,1,appid,mchId,key,"ksdy");

		orderQuery(wxorderNo, appid, mchId, key);

	}

	/**
	 * 退款状态查询
	 * 
	 * @param outTradeNo
	 *            订单号
	 * @return
	 */
	public static RefundStatus refundQuery(String outTradeNo, String appid,
			String mchid, String key) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("transaction_id", outTradeNo);
		params.put("appid", appid);
		params.put("mch_id", mchid);
		params.put("nonce_str", PayUtils.createNoncestr());
		params.put("sign", PayUtils.getSign(params,key));

		String xml = XmlUtils.mapToXml(params);
		
		InputStream in=PayUtils.class.getResourceAsStream("apiclient_cert.p12");
		String result = PayUtils
				.postXmlSSLCurl(xml, PayConfig.REFUND_QUERY_URL,mchid,in);
		Map<String, String> resultMap = XmlUtils.xmlToMap(result);
		if (null == resultMap.get("return_code"))
			return RefundStatus.ERROR;
		if (!"SUCCESS".equals(resultMap.get("return_code")))
			return RefundStatus.ERROR;
		String status = resultMap.get("refund_status_" + 0);
		return RefundStatus.valueOf(status);
	}

	/**
	 * 校验请求
	 * 
	 * @param xml
	 * @return
	 */
	public static boolean checkSign(Map<String, String> params,
			String privatekey) {
		Map<String, String> map = new HashMap<String, String>(params);
		String key = "sign";
		String sign = map.get(key);
		if (StringUtils.isBlank(sign))
			return false;
		map.remove(key);
		return sign.equals(getSign(map, privatekey));
		// return false;
	}

	/**
	 * 得到付款签名
	 * 
	 */

	public static String getPaySign(SortedMap<String, String> packageParams,
			String key) {
		StringBuffer sb = new StringBuffer();
		Set es = packageParams.entrySet();
		Iterator<Map.Entry> it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k)
					&& !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + key);
		String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();

		return sign;

	}

	/**
	 * 得到微信的预付ID
	 */

	public static String getPrepayId(TreeMap<String, String> treeMap, String key) {

		String prepay_id = "";
		String sign = "";
		StringBuilder sb = new StringBuilder();
		for (String key1 : treeMap.keySet()) {

			sb.append(key1).append("=").append(treeMap.get(key1)).append("&");
			// }
		}
		sb.append("key=" + key);
		try {
			sign = DigestUtils.md5Hex(sb.toString().getBytes("utf-8"))
					.toUpperCase();
		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		treeMap.put("sign", sign);
		StringBuilder xml = new StringBuilder();
		xml.append("<xml>\n");

		for (Map.Entry<String, String> entry : treeMap.entrySet()) {
			if ("body".equals(entry.getKey()) || "sign".equals(entry.getKey())) {
				xml.append("<" + entry.getKey() + "><![CDATA[")
						.append(entry.getValue())
						.append("]]></" + entry.getKey() + ">\n");
			} else {
				xml.append("<" + entry.getKey() + ">").append(entry.getValue())
						.append("</" + entry.getKey() + ">\n");
			}
		}

		xml.append("</xml>");
		
		log.info("httpRequest:"+xml.toString());
		
		// 创建HttpClientBuilder
		HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
		// HttpClient
		CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
		HttpPost httpPost = new HttpPost(PayConfig.CURL_URL);

		StringEntity entity;
		try {
			entity = new StringEntity(xml.toString(), "utf-8");
			httpPost.setEntity(entity);
			HttpResponse httpResponse;
			httpResponse = closeableHttpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			if (httpEntity != null) {
				String result = EntityUtils.toString(httpEntity, "UTF-8");
				log.info("httpResponse:"+result);
				Map<String, String> hansh = XmlUtils.xmlToMap(result);
				prepay_id = hansh.get("prepay_id");

				if (prepay_id == null) {
					return null;
				}
			}
			// 释放资源
			closeableHttpClient.close();
		} catch (Exception e) {
			return null;
			// e.printStackTrace();
		}

		return prepay_id;
	}

	/**
	 * 接受到回调后的返回
	 * 
	 * @param success
	 * @param msg
	 * @return
	 */
	public static String returnXml(boolean success, String msg) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("return_code", success ? "SUCCESS" : "FAIL");
		if (StringUtils.isNotBlank(msg))
			map.put("return_msg", msg);
		return XmlUtils.mapToXml(map);
	}
	// public static void main(String[] args) {
	// String openid = "op7X8tz1Ug0J2khfm8jP-UiDKacw";
	// BigDecimal totalFee = new BigDecimal(0.01);
	// String body = "测试，共享";
	// WeixinOrder weixinOrder = new WeixinOrder();
	//
	// weixinOrder.setIp("116.21.163.170");
	// weixinOrder.setCreateTime(new Date());
	// weixinOrder.setOpenid(openid);
	// weixinOrder.setOutTradeNo(UUID.randomUUID().toString().replaceAll("\\-",
	// ""));
	// weixinOrder.setTotalFee(totalFee);
	// weixinOrder.setTradeStatus(TradeStatus.PRAPARE);
	// weixinOrder.setTradeType(PayUtils.JSAPI);
	// weixinOrder.setBody(body);
	//
	// String nonceStr = PayUtils.createNoncestr();
	// if (log.isInfoEnabled()) log.info("nonceStr:"+nonceStr);
	// if (log.isInfoEnabled())
	// log.info("OutTradeNo:"+weixinOrder.getOutTradeNo()+",body:"+weixinOrder.getBody()+",totalFee"+weixinOrder.getTotalFee().floatValue()+", PayConfig.NOTIFY_URL:"+PayConfig.NOTIFY_URL+",tradeType:"+weixinOrder.getTradeType()+", openid:"+openid+", Ip:"+weixinOrder.getIp());
	// String sign = PayUtils.getSign(nonceStr, weixinOrder.getOutTradeNo(),
	// weixinOrder.getBody(),
	// String.valueOf(weixinOrder.getTotalFee().multiply(new
	// BigDecimal(100)).intValue()), PayConfig.NOTIFY_URL,
	// weixinOrder.getTradeType(), openid, weixinOrder.getIp());
	// if (log.isInfoEnabled()) log.info("sign:"+sign);
	// String prepayId = PayUtils.getPrepayId(nonceStr,
	// weixinOrder.getOutTradeNo(), weixinOrder.getBody(),
	// String.valueOf(weixinOrder.getTotalFee().multiply(new
	// BigDecimal(100)).intValue()), weixinOrder.getTradeType(),
	// weixinOrder.getOpenid(), weixinOrder.getIp(), sign);
	// if (log.isInfoEnabled()) log.info("prepayId:"+prepayId);
	// weixinOrder.setPrepayId(prepayId);
	// }
}
